﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Xml.Linq;
using RoMBot.Properties;

namespace RoMBot.Database
{
    public enum NodeType
    {
        None = 0,
        Wood = 1,
        Ore = 2,
        Herb = 3
    }

    public enum ConsumableType
    {
        None,
        MoT,
        HoT,
        Heal,
        Mana,
        Arrow,
        ArrowQuiver,
        Thrown,
        ThrownBag,
        Poison,
        EggPetHoe,
        EggPetHatchet,
        EggPetSpade,
        PhirusBoth,
        PhirusHealing,
        PhirusMana
    }

    public static class Database
    {
        public static readonly Dictionary<int, Node> Nodes = new Dictionary<int, Node>();
        public static readonly Dictionary<int, Consumable> Consumables = new Dictionary<int, Consumable>();
        public static readonly Dictionary<int, GiftBag> GiftBags = new Dictionary<int, GiftBag>();
        //private static readonly IDocumentStore DocumentStore;
        //private static readonly Lazy<IDocumentStore> TheDocStore = new Lazy<IDocumentStore>( () =>
        //                                                                                     {
        //                                                                                         IDocumentStore documentStore = null;
        //                                                                                         bool success;
        //                                                                                         try
        //                                                                                         {
        //                                                                                             documentStore = new DocumentStore
        //                                                                                             {
        //                                                                                                 Url = "http://localhost:8080"
        //                                                                                             }.Initialize();
        //                                                                                             var task = documentStore.AsyncDatabaseCommands.GetBuildNumberAsync();
        //                                                                                             success = task.Wait( 500 );
        //                                                                                         }
        //                                                                                         catch
        //                                                                                         {
        //                                                                                             success = false;
        //                                                                                         }
        //                                                                                         if ( !success )
        //                                                                                         {
        //                                                                                             NonAdminHttp.EnsureCanListenToWhenInNonAdminContext( 8080 );

        //                                                                                             documentStore = new EmbeddableDocumentStore
        //                                                                                             {
        //                                                                                                 DataDirectory = Bot.DataPath + "Database",
        //                                                                                                 UseEmbeddedHttpServer = true
        //                                                                                             }.Initialize();
        //                                                                                         }

        //                                                                                         return documentStore;
        //                                                                                     } );

        //public static IDocumentStore DocumentStore
        //{
        //    get { return TheDocStore.Value; }
        //}

        public static void StartDatabase()
        {
            //This is just to make the lazy instantiation kick in...
            //Task.Factory.StartNew( () =>
            //                       {
            //                           Bot.Debug( "Initializing Database" );
            //                           var d = DocumentStore;
            //                           Bot.Debug( "Done initializing Database" );
            //                       } );
        }

        static Database()
        {
        }

        public static Dictionary<int, DatabaseItemType> ItemTypes
        {
            get { return _itemTypes.Items; }
        }

        private static DatabaseItemTypesCollection _itemTypes = new DatabaseItemTypesCollection();
        public static void LoadItemTypes()
        {
            var lang = ( string ) Helpers.RoMScript( "GetLanguage()" );

            if ( File.Exists( Bot.DataPath + "Database\\ItemTypes.xml" ) )
            {
                var docItemTypes = XDocument.Load( Bot.DataPath + "Database\\ItemTypes.xml" );
                var itemTypesElement = docItemTypes.Element( "itemtypes" );
                if ( itemTypesElement != null )
                {
                    _itemTypes.Language = itemTypesElement.Attribute( "language" ).Value;
                    _itemTypes.Version = Convert.ToInt32( itemTypesElement.Attribute( "version" ).Value );
                    if ( _itemTypes.Language == lang && _itemTypes.Version == 1 )
                    {
                        foreach ( var element in itemTypesElement.Elements( "itemtype" ) )
                        {
                            var id = Convert.ToInt32( element.Attribute( "key" ).Value );
                            _itemTypes.Add( id, new DatabaseItemType { Name = element.Attribute( "name" ).Value, Items = new Dictionary<int, DatabaseItemType>() } );
                            foreach ( var itemsElement in element.Elements( "items" ) )
                            {
                                var itemId = Convert.ToInt32( itemsElement.Attribute( "key" ).Value );
                                _itemTypes[ id ].Items.Add( itemId, new DatabaseItemType { Name = itemsElement.Attribute( "name" ).Value, Items = new Dictionary<int, DatabaseItemType>() } );
                                foreach ( var itemElement in itemsElement.Elements( "item" ) )
                                {
                                    var subItemId = Convert.ToInt32( itemElement.Attribute( "key" ).Value );
                                    _itemTypes[ id ].Items[ itemId ].Items.Add( subItemId, new DatabaseItemType { Name = itemElement.Attribute( "name" ).Value, UniqueName = itemElement.Attribute( "uniquename" ).Value, Items = new Dictionary<int, DatabaseItemType>() } );
                                }
                            }
                        }

                        return;
                    }
                }
            }

            //If items are not in database yet or language changed, instantiate from game.
            _itemTypes.Language = lang;
            _itemTypes.Version = 1;
            try
            {
                var t1_data = Helpers.RoMCode( "A=0 repeat n='AC_ITEMTYPENAME_'..A T=TEXT(n)" +
                               " if n==T then break end table.insert(a,A..','..T) A=A+1 until false" ) as List<object>;
                if ( t1_data != null )
                {
                    foreach ( string o in t1_data )
                    {
                        var vals = o.Split( ',' );
                        int id;
                        if ( int.TryParse( vals[ 0 ], out id ) )
                        {
                            _itemTypes.Add( id, new DatabaseItemType { Name = vals[ 1 ], Items = new Dictionary<int, DatabaseItemType>() } );
                        }
                        Bot.Debug( o );
                    }

                    var t2_data = Helpers.RoMCode( "for A=0," + _itemTypes.Count + " do B=0 repeat " +
                                                   "n='AC_ITEMTYPENAME_'..A..'_'..B T=TEXT(n) if n==T then break end " +
                                                   "table.insert(a,A..','..B..','..T) B=B+1 until false end" ) as List<object>;
                    if ( t2_data != null )
                    {
                        foreach ( string o in t2_data )
                        {
                            var vals = o.Split( ',' );
                            if ( string.IsNullOrWhiteSpace( vals[ 0 ] ) )
                            {
                                break;
                            }
                            int id;
                            if ( int.TryParse( vals[ 0 ], out id ) )
                            {
                                if ( !_itemTypes[ id ].Items.ContainsKey( Convert.ToInt32( vals[ 1 ] ) ) )
                                {
                                    _itemTypes[ id ].Items.Add( Convert.ToInt32( vals[ 1 ] ), new DatabaseItemType { Name = vals[ 2 ], Items = new Dictionary<int, DatabaseItemType>() } );
                                }
                            }
                        }

                        for ( int t1 = 0; t1 < _itemTypes.Count; t1++ )
                        {
                            var t3_data = Helpers.RoMCode( "for B=0," + _itemTypes[ t1 ].Items.Count + " do C=0 repeat " +
                                                           "n='AC_ITEMTYPENAME_" + t1 + "_'..B..'_'..C T=TEXT(n) if n==T then break end " +
                                                           "table.insert(a,B..','..C..','..T) C=C+1 until false end" ) as List<object>;
                            if ( t3_data != null )
                            {
                                foreach ( string o in t3_data )
                                {
                                    var vals = o.Split( ',' );
                                    if ( vals.Length < 2 )
                                    {
                                        break;
                                    }
                                    int t2, t3;
                                    if ( !int.TryParse( vals[ 0 ], out t2 ) | !int.TryParse( vals[ 1 ], out t3 ) )
                                    {
                                        continue;
                                    }

                                    //t2 = Convert.ToInt32( vals[ 0 ] );
                                    //t3 = Convert.ToInt32( vals[ 1 ] );
                                    var item = new DatabaseItemType { Name = vals[ 2 ] };
                                    if ( ( t1 == 0 && t2 >= 0 & t2 <= 4 ) || // non unique t3 weapon name
                                         ( t1 == 1 && t2 >= 0 & t2 <= 3 ) )  // non unique t3 armor name
                                    {
                                        // Add the t2 name to make a unique name
                                        item.UniqueName = item.Name + " " + _itemTypes[ t1 ].Items[ t2 ].Name;
                                    }
                                    if ( !_itemTypes[ t1 ].Items[ t2 ].Items.ContainsKey( t3 ) )
                                    {
                                        _itemTypes[ t1 ].Items[ t2 ].Items.Add( t3, item );
                                    }
                                }

                            }
                        }
                        // Get exceptions
                        // Exceptions:
                        _itemTypes[ 3 ].Items[ 5 ] = new DatabaseItemType { Name = ( string ) Helpers.RoMScript( "TEXT(\"SYS_ITEMTYPE_14\")" ) }; // Prepared Materials
                    }
                    var doc = new XDocument(
                            new XElement( "itemtypes",
                                          new XAttribute( "language", _itemTypes.Language ),
                                          new XAttribute( "version", _itemTypes.Version )
                                    ) );
                    foreach ( var itemType in ItemTypes )
                    {
                        var it = new XElement( "itemtype",
                                               new XAttribute( "key", itemType.Key ),
                                               new XAttribute( "name", itemType.Value.Name ),
                                               new XAttribute( "uniquename", itemType.Value.UniqueName ?? "" ) );
                        foreach ( var item in itemType.Value.Items )
                        {
                            var child = new XElement( "items",
                                                      new XAttribute( "key", item.Key ),
                                                      new XAttribute( "name", item.Value.Name ),
                                                      new XAttribute( "uniquename", item.Value.UniqueName ?? "" ) );
                            if ( item.Value.Items != null )
                            {
                                foreach ( var type in item.Value.Items )
                                {
                                    var subchild = new XElement( "item",
                                                                 new XAttribute( "key", type.Key ),
                                                                 new XAttribute( "name", type.Value.Name ),
                                                                 new XAttribute( "uniquename", type.Value.UniqueName ?? "" ) );
                                    child.Add( subchild );
                                }
                            }
                            it.Add( child );
                        }
                        doc.Element( "itemtypes" ).Add( it );
                    }
                    doc.Save( Bot.DataPath + "Database\\ItemTypes.xml" );
                    //using ( var session = DocumentStore.OpenSession() )
                    //{
                    //    session.Store( _itemTypes, "ItemTypes/" + lang );
                    //    session.SaveChanges();
                    //}
                }
            }
            catch ( Exception e )
            {
                Bot.Error( "Error when loading ItemTypes" + e.Message );
            }
        }

        public static void Load()
        {
            LoadItemTypes();

            Nodes.Clear();
            Consumables.Clear();
            //using ( var session = DocumentStore.OpenSession() )
            //{
            //    var loadedNodes = session.Query<Node>().Where( n => n.Id > 0 );
            //    var loadedConsumables = session.Query<Consumable>().Where( c => c.Id > 0 );
            //    foreach ( var node in loadedNodes )
            //    {
            //        Nodes.Add( node.Id, node );
            //    }

            //    foreach ( var consumable in loadedConsumables )
            //    {
            //        Consumables.Add( consumable.Id, consumable );
            //    }
            //}
            //if ( Nodes.Count > 0 && Consumables.Count > 0 )
            //{
            //    return;
            //}

            var xdocument = XDocument.Parse( Resources.nodes );
            //<node name="Zinc Ore" id="560001" type="ORE" level="1" />
            var nodes = xdocument.Element( "nodes" );
            if ( nodes != null )
            {
                foreach ( var node in nodes.Elements( "node" ) )
                {
                    var id = Convert.ToInt32( node.Attribute( "id" ).Value );
                    if ( !Nodes.ContainsKey( id ) )
                    {
                        Nodes.Add( id, new Node { Name = node.Attribute( "name" ).Value, Type = ParseType( node.Attribute( "type" ).Value ), Id = id, Level = Convert.ToInt32( node.Attribute( "level" ).Value ) } );
                    }
                }
            }

            //using ( var session = DocumentStore.OpenSession() )
            {
                //foreach ( var node in Nodes.Values )
                //{
                //    session.Store( node );
                //}

                xdocument = XDocument.Parse( Resources.consumables );
                nodes = xdocument.Element( "potions" );
                //<potion type="hot" id="200663" level="1" potency="200" name="Simple First Aid Potion"/>
                if ( nodes != null )
                {
                    foreach ( var node in nodes.Elements( "potion" ) )
                    {
                        var id = Convert.ToInt32( node.Attribute( "id" ).Value );
                        if ( !Consumables.ContainsKey( id ) )
                        {
                            Consumables.Add( id, new Consumable
                                                 {
                                                     Name = node.Attribute( "name" ).Value,
                                                     Type = ParseConsumableType( node.Attribute( "type" ).Value ),
                                                     Id = id,
                                                     Level = Convert.ToInt32( node.Attribute( "level" ) != null ? node.Attribute( "level" ).Value : "1" ),
                                                     Potency = Convert.ToInt32( node.Attribute( "potency" ) != null ? node.Attribute( "potency" ).Value : "0" )
                                                 } );
                        }
                    }
                }

                //foreach ( var consumable in Consumables.Values )
                //{
                //    session.Store( consumable );
                //}

                //session.SaveChanges();
            }
        }

        private static NodeType ParseType( string type )
        {
            NodeType t;
            return Enum.TryParse( type, true, out t ) ? t : NodeType.None;
        }

        private static ConsumableType ParseConsumableType( string type )
        {
            ConsumableType t;
            return Enum.TryParse( type.Replace( "_", "" ), true, out t ) ? t : ConsumableType.None;
        }
    }

    public class DatabaseItemType
    {
        public string Name { get; set; }

        public string UniqueName { get; set; }

        public Dictionary<int, DatabaseItemType> Items { get; set; }

        protected bool ShouldSerializeItems()
        {
            return Items != null && Items.Count > 0;
        }
    }

    public class DatabaseItemTypesCollection
    {
        private Dictionary<int, DatabaseItemType> items = new Dictionary<int, DatabaseItemType>();

        public string Language { get; set; }
        public int Version { get; set; }

        public int Count
        {
            get { return items.Count; }
        }

        public void Add( int id, DatabaseItemType item )
        {
            items.Add( id, item );
        }

        public DatabaseItemType this[ int id ]
        {
            get { return items[ id ]; }
        }

        public Dictionary<int, DatabaseItemType> Items
        {
            get { return items; }
            set { items = value; }
        }
    }
}
